#include <stdio.h>
#include <stdlib.h>
#include <elf.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <stdbool.h>
#include <string.h>

typedef Elf32_Ehdr Ehdr;
typedef Elf32_Shdr Shdr;
typedef Elf32_Half Half;
typedef Elf32_Off Off;
typedef Elf32_Word Word;
typedef Elf32_Sym Sym;
typedef Elf32_Addr Addr;

#define MAXFUNCNAME 32 //function name should not less than this
#define MAXFUNCNUM  32 //function number should not less than this
typedef struct FunctionSymbol
{
    char name[MAXFUNCNAME]; 
    Addr offset;
} FunctionSymbol;
FunctionSymbol ftrace_funcSym[MAXFUNCNUM];
int func_num = 0;

/* ======== read from file && store it to structure ========*/
/* Read ELF Header */
void Read_ELFHeader(int fd, Ehdr *elfheader)
{
    if(read(fd, elfheader, sizeof(Ehdr)) == -1) 
        perror("read elf header");
}

/* Read Section Header Table [index] */
void Read_SectionHeaderTable(int fd, Shdr *sectionheader, Off offset, int index)
{
    lseek(fd, offset + index * sizeof(Shdr), SEEK_SET);
    if(read(fd, sectionheader, sizeof(Shdr)) == -1) 
        perror("read section header table");
}

/* Read Symbol Table [index]*/
void Read_SymbolTable(int fd, Sym *sym, Off sym_off, int index)
{
    lseek(fd, sym_off + index * sizeof(Sym), SEEK_SET);
    if(read(fd, sym, sizeof(Sym)) == -1)
        perror("read symbol table");
}

/* Read */
void Read(int fd, char *buf, Off offset, Word size)
{
    lseek(fd, offset, SEEK_SET);
    if(read(fd, buf, size) == -1)
        perror("read");
}
/* ========================================== */


/* ======== Get infomation from elf header ========*/
/* Get Magic number */
unsigned char* GetMagic(Ehdr *elfheader)
{
    return elfheader->e_ident;
}

/* Get Section Header Table Entry */
Off GetSeHEntry(Ehdr *elfheader)
{
    return elfheader->e_shoff;
}

/* Get the Number of Section Header index */
Half GetSeHNum(Ehdr *elfheader)
{
    return elfheader->e_shnum;
}

/* Get Section header string table(shstrtab)'s index in Section Header table*/
Half GetShStrIndex(Ehdr *elfheader)
{
    return elfheader->e_shstrndx;
}
/* ========================================== */


/* ======== Get infomation from section header table ======== */
/* Get section entry */
Off GetSecEntry(Shdr *shdr)
{
    return shdr->sh_offset;
}

/* Get section size */
Word GetSecSize(Shdr *shdr)
{
    return shdr->sh_size;
}
/* ========================================== */


/* ======== Get infomation from Symbol Table ======== */
/* Get Symbol Type */
char GetSymType(Sym *sym)
{
    return sym->st_info & 0xF;
}

char* GetSymName(int fd, Sym *sym, Off str_off, Word str_size)
{
    char *symbolNameStart = (char *)malloc(sizeof(char) * str_size);
    Read(fd, symbolNameStart, str_off, str_size);
    Word nameIdx = sym->st_name;
    free(symbolNameStart);
    return &symbolNameStart[nameIdx];
}

/* Judge if the symbol is a function */
bool IsSymFunction(char type)
{
    if(type == STT_FUNC) return 1;
    else return 0;
}
/* ========================================== */


/* Get All function name and Offset in the file */
void GetAllFunctionNameOffset(char *filepath)
{
    int fd = open(filepath, O_RDONLY);
    if(fd == -1) perror("open");

    Ehdr elf_hdr;
    Read_ELFHeader(fd, &elf_hdr);

    Off sec_hdr_tab_off = GetSeHEntry(&elf_hdr);
    Half str_sec_idx = GetShStrIndex(&elf_hdr);
    Shdr str_tab, sym_tab;

    //Shdr shstr_sec_hdr_tab;
    // Read_SectionHeaderTable(fd, &shstr_sec_hdr_tab, sec_hdr_tab_off, str_sec_idx);
    // Off shstr_off = GetSecEntry(&shstr_sec_hdr_tab);
    // Word shstr_size = GetSecSize(&shstr_sec_hdr_tab);
    // Show(fd, shstr_off, shstr_size);

    Read_SectionHeaderTable(fd, &str_tab, sec_hdr_tab_off, str_sec_idx - 1);
    Off str_off = GetSecEntry(&str_tab);
    Word str_size = GetSecSize(&str_tab);
    //Show(fd, str_off, str_size);

    Read_SectionHeaderTable(fd, &sym_tab, sec_hdr_tab_off, str_sec_idx - 2);
    Off sym_off = GetSecEntry(&sym_tab);
    Word sym_tab_size = GetSecSize(&sym_tab);
    int sym_num = sym_tab_size / sizeof(Sym);

    for(int i = 0; i < sym_num; i++)
    {
        Sym sym;
        Read_SymbolTable(fd, &sym, sym_off, i);
        if(IsSymFunction(GetSymType(&sym)))
        {
            strcpy(ftrace_funcSym[func_num].name, GetSymName(fd, &sym, str_off, str_size)); 
            ftrace_funcSym[func_num++].offset = sym.st_value;
            printf("Name:%s -- Address:%016x\n", ftrace_funcSym[func_num - 1].name, ftrace_funcSym[func_num - 1].offset);
        }
    }
    close(fd);
}


/*for test*/
void ShowMagic(unsigned char *magic)
{
    for(int i = 0; i < 4; i++)
        printf("%02x(%c) ",magic[i], magic[i]);
    printf("\n");
}
void test01(Ehdr *elf_hdr)
{
    unsigned char *magic;
    magic = GetMagic(elf_hdr);
    ShowMagic(magic);
}
void Show(int fd, Off offset, Word size)
{
    char *buf = (char *)malloc(sizeof(char) * size);
    Read(fd, buf, offset, size);
    for(int i = 0; i < size; i++)
    {
        printf("%c",buf[i]);
    }
    printf("\n");
    free(buf);
}
/*end test*/


int main(int argc, char **argv)
{
    if(argc != 2) {
        fprintf(stderr, "Usage:%s <ELF file>\n", argv[0]);
        return 1;
    }
    GetAllFunctionNameOffset(argv[1]);
    return 0;
}
